package com.tanx.cqrs.infrastructure.spring.saga;

import com.tanx.cqrs.event.Event;
import com.tanx.cqrs.event.store.EventStoreRepository;
import com.tanx.cqrs.saga.*;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * saga处理器的解析器解析器
 */
public class SpringBeanSagaHandlerResolverImpl implements SagaHandlerResolver {
    private Map<Class<?>, SagaHandlerInstance> resolvers = new HashMap<>();
    private ApplicationContext context;

    public SpringBeanSagaHandlerResolverImpl(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void registerAllHandler() {
        Map<String, Saga> beansOfType = context.getBeansOfType(Saga.class);
        for (Saga bean : beansOfType.values()) {
            Class<?> beanClass = bean.getClass();
            SagaHandler classHandler = beanClass.getAnnotation(SagaHandler.class);
            //如果是类上存在注解,那么此类都作为saga处理器,其中的所有方法都作为处理器
            if (classHandler != null) {
                Method[] methods = beanClass.getMethods();
                addInstances(bean, methods);
                continue;
            }
            Method[] methods = bean.getClass().getMethods();
            for (Method method : methods) {
                if (method.getAnnotation(SagaHandler.class) != null || method.getAnnotation(SagaStart.class) != null
                        || method.getAnnotation(EndSaga.class) != null) {
                    addInstances(bean, method);
                }
            }
        }
    }

    private void addInstances(Saga bean, Method... methods) {
        SagaHandlerInstance instance;
        for (Method method : methods) {
            Parameter parameter = getParameterClass(method);
            instance = new SagaHandlerInstance(bean, method, getRepository(bean), getTimeOut(bean, method));
            resolvers.put(parameter.getType(), instance);
        }
    }

    private long getTimeOut(Saga bean, Method method) {
        SagaHandler handler = method.getAnnotation(SagaHandler.class);
        if (handler != null) {
            return handler.timeout();
        }
        SagaStart start = method.getAnnotation(SagaStart.class);
        handler = start.getClass().getAnnotation(SagaHandler.class);
        if (handler != null) {
            return handler.timeout();
        }
        EndSaga end = method.getAnnotation(EndSaga.class);
        handler = end.getClass().getAnnotation(SagaHandler.class);
        if (handler != null) {
            return handler.timeout();
        }
        SagaHandler annotation = bean.getClass().getAnnotation(SagaHandler.class);
        if (annotation != null) {
            return annotation.timeout();
        }
        return -1;
    }

    private SagaStoreRepository getRepository(Saga bean) {
        Class<?> beanClass = bean.getClass();
        SagaRepository annotation = beanClass.getAnnotation(SagaRepository.class);
        if (annotation != null && !StringUtils.isEmpty(annotation.getBeanName())) {
            return context.getBean(annotation.getBeanName(), SagaStoreRepository.class);
        }
        //查找泛型bean
        Collection<SagaStoreRepository> beans = context.getBeansOfType(SagaStoreRepository.class).values();
        if (beans != null && !beans.isEmpty()) {
            for (SagaStoreRepository beanTemp : beans) {
                ResolvableType resolvableType = ResolvableType.forClass(beanTemp.getClass());
                for (ResolvableType type : resolvableType.getInterfaces()) {
                    if (type.isAssignableFrom(EventStoreRepository.class) && type.getGeneric(0).resolve().equals(beanClass)) {
                        return beanTemp;
                    }
                }
            }
        }
        return null;
    }

    private Parameter getParameterClass(Method method) {
        Parameter[] parameters = method.getParameters();
        if (parameters.length != 1) {
            throw new RuntimeException("方法:" + method.getName() + "参数只能为1个,并且只能为Event实现");
        }
        Parameter parameter = parameters[0];
        if (parameter.getType().isAssignableFrom(Event.class)) {
            throw new RuntimeException("方法:" + method.getName() + "参数只能为Event实现");
        }
        return parameter;
    }

    @Override
    public void handlerEvent(Event event) {
        if (!isSagaEvent(event)) {
            return;
        }
        //获取saga处理器
        SagaHandlerInstance sagaHandlerInstance = resolvers.get(event.getClass());
        //处理事件
        sagaHandlerInstance.invoke(event);
    }

    private boolean isSagaEvent(Event event) {
        return resolvers.containsKey(event.getClass());
    }
}
